package com.jmxcfc.blfsc.huisheng.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jmxcfc.blfsc.common.common.enums.ProcessStateEnum;
import com.jmxcfc.blfsc.common.common.request.BaseRequest;
import com.jmxcfc.blfsc.common.common.response.BaseResponse;
import com.jmxcfc.blfsc.common.exception.*;
import com.jmxcfc.blfsc.common.sms.BaseContext;
import com.jmxcfc.blfsc.common.sms.SmsConstants;
import com.jmxcfc.blfsc.common.sms.VoiceRequest;
import com.jmxcfc.blfsc.common.sms.extend.VoiceExtend;
import com.jmxcfc.blfsc.common.sms.vo.FailListItem;
import com.jmxcfc.blfsc.common.validation.rule.RequestFieldRule;
import com.jmxcfc.blfsc.common.validation.rule.SmsRulesUtil;
import com.jmxcfc.blfsc.huisheng.common.Constants;
import com.jmxcfc.blfsc.huisheng.entity.SendDetailSub;
import com.jmxcfc.blfsc.huisheng.service.ICallListRequestService;
import com.jmxcfc.blfsc.huisheng.service.ISendDetailSubService;
import com.jmxcfc.blfsc.huisheng.service.SignatureService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 * 语音业务主表 服务实现类
 * 慧声语音的客户名单导入
 * </p>
 *
 * @author pbs
 * @since 2023-09-23 5:51:38
 */
@Slf4j
@Service
@RefreshScope
public class CallListRequestServiceImpl implements ICallListRequestService {

    //请求地址
    @Value("${huisheng.url}")
    private String url;

    //应用ID
    @Value("${huisheng.appId}")
    private String appId;

    //导入客户名单列表接口名称
    @Value("${huisheng.quickCall}")
    private String quickCall;




    private ISendDetailSubService sendDetailSubService;

    private RestTemplate restTemplate;

    private SignatureService signatureService;



    @Autowired
    public void setSendDetailSubService(ISendDetailSubService sendDetailSubService) {
        this.sendDetailSubService = sendDetailSubService;
    }

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Autowired
    public void setSignatureService(SignatureService signatureService) {
        this.signatureService = signatureService;
    }

    @Override
    public BaseResponse<JSONObject> sendRequest(BaseRequest<VoiceRequest<VoiceExtend>> requestMessage) {
        BaseContext<String> baseContext = new BaseContext<>();
        //头部参数校验规则:业务号、请求流水号字段
        List<RequestFieldRule<?>> headerElementRules = baseContext.getHeaderElementRules();
        headerElementRules.add(SmsRulesUtil.getRequestNoRule());
        //请求体参数校验:发送集合
        List<RequestFieldRule<?>> bodyElementRules = baseContext.getBodyElementRules();
        bodyElementRules.add(SmsRulesUtil.getSendDtlRule());
        bodyElementRules.add(SmsRulesUtil.getTaskIdlRule());
        return execute(requestMessage, baseContext);
    }


    @Override
    public <E> void validBodyExtend(VoiceRequest<VoiceExtend> body, BaseContext<E> baseHandleContext) throws ValidBodyException {
    }


    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void beforeOutbound(BaseRequest<VoiceRequest<VoiceExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws BeforeOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try {
            //
            String batchId = request.getHeader().getSerialSeq();
            log.info("慧声语音请求业务号requestNo:{},requestId请求Id(批次号):{},慧声语音前置处理:数据落库和参数组装",requestNo,batchId);
            List<SendDetailSub> sendDetailSubList = new ArrayList<>();
            //参数组装
            String requestParam = buildParam(request, sendDetailSubList);
            baseContext.setRestHttpEntity(creatRequestEntity(requestParam));
            //保存数据
            saveBatch(requestNo,sendDetailSubList);
            log.info("慧声语音请求业务号requestNo:{},requestId请求Id(批次号):{},保存慧声语音明细表数据条数:{},前置处理结束", requestNo, batchId, sendDetailSubList.size());
        } catch (Exception e) {
            log.error("慧声语音请求业务号requestNo:{},前置处理异常:{}", requestNo, e.getMessage(), e);
            throw new BeforeOutboundException(e.getMessage(), e);
        }
    }

    /**
     * 外呼操作
     * @param request
     * @param response
     * @param baseContext
     * @throws OutboundException
     */
    @Override
    public void outbound(BaseRequest<VoiceRequest<VoiceExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws OutboundException {
        try {
            log.info("慧声语音请求业务号requestNo:{},请求外呼,请求地址:{},请参数:{}",request.getHeader().getRequestNo(),url,baseContext.getRestHttpEntity());
            String resp = restTemplate.postForObject(url, baseContext.getRestHttpEntity(), String.class);
            log.info("慧声语音请求业务号requestNo:{},请求外呼,响应内容:{}", request.getHeader().getRequestNo(), resp);
            baseContext.setRestResponse(resp);
        } catch (Exception e) {
            log.error("慧声语音请求业务号requestNo:{},请求外呼异常:{}", request.getHeader().getRequestNo(), e.getMessage(),e);
            throw new OutboundException(e.getMessage(), e);
        }
    }


    /**
     * 响应内容解析
     * @param request
     * @param response
     * @param baseContext
     * @throws AfterOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void afterOutbound(BaseRequest<VoiceRequest<VoiceExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws AfterOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        String serialSeq = request.getHeader().getSerialSeq();
        try {
            JSONObject restResponse = JSON.parseObject(baseContext.getRestResponse());
            log.info("慧声语音请求业务号requestNo:{},后置处理:解析响应结果:{}", requestNo,restResponse);
            JSONObject restContent = JSON.parseObject(restResponse.getString(Constants.RESP_CONTENT));

            //响应blfs-sms模块的扩展字段
            JSONObject responseBody = new JSONObject();
            //响应blfs-sms结果
            String message = "全部成功";
            //有对接异步接口,等待接收对方异步状态通知
            response.getHeader().setProcessState(ProcessStateEnum.NOTIFICATION);

            String code = Optional.ofNullable(restContent.getString(Constants.RESP_CODE)).orElse("");
            String msg = Optional.ofNullable(restContent.getString(Constants.RESP_MSG)).orElse("");
            String subCode = Optional.ofNullable(restContent.getString(Constants.RESP_SUB_CODE)).orElse("");
            String subMsg = Optional.ofNullable(restContent.getString(Constants.RESP_SUB_MSG)).orElse("");
            JSONObject date = JSON.parseObject(restContent.getString(Constants.RESP_DATA));

            /**
             * 以返回结果更新全部数据
             更新数据规则:code不等于20000或sub_code不等于200 则接口调用失败,更新全部数据为失败
             * */
            updateSendDetailSub(requestNo,serialSeq, code, msg, subCode,subMsg);
            if (!(Constants.CODE_SUCCESS.equals(code) || Constants.SUB_CODE_SUCCESS.equals(subCode)) ) {
                response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
                message = "全部失败";
            }else{
                //存在部分失败数据时，则在把失败数据更新
                List<JSONObject > responseFailDataList = JSON.parseArray(date.getString(Constants.RESP_FAIL_DETAIL),JSONObject.class);
                if (CollectionUtils.isNotEmpty(responseFailDataList)) {
                    log.warn("慧声语音请求业务号requestNo:{},语音响应,存在部分失败号码,data:{}",requestNo,responseFailDataList);
                    List<FailListItem> failListItemList = new ArrayList<>(responseFailDataList.size());
                    List<SendDetailSub> updateSendDetailSubList = new ArrayList<>(responseFailDataList.size());
                    for (JSONObject failJSONObject : responseFailDataList) {
                        String caseId = Optional.ofNullable(failJSONObject.getString(Constants.RESP_CASE_ID)).orElse("");
                        SendDetailSub sendDetailSub = new SendDetailSub();
                        sendDetailSub.setUuid(caseId);
                        sendDetailSub.setFailMsg(Optional.ofNullable(failJSONObject.getString(Constants.RESP_MSG)).orElse(""));
                        updateSendDetailSubList.add(sendDetailSub);

                        FailListItem failListItem = new FailListItem();
                        failListItem.setUuid(caseId);
                        failListItem.setErrorDesc(Optional.ofNullable(failJSONObject.getString(Constants.RESP_MSG)).orElse(""));
                        failListItemList.add(failListItem);
                    }
                    message = "存在全部或部分失败";
                    response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
                    //失败列表数据
                    responseBody.put(SmsConstants.RESP_FAIL_LIST, failListItemList);
                    Optional.ofNullable(CollectionUtils.isEmpty(updateSendDetailSubList)?null:updateSendDetailSubList).ifPresent(
                            sendDetailSubs ->{
                                log.warn("慧声语音请求业务号requestNo:{},更新HUISHENG_SEND_DETAIL_SUB 表,记录导入失败的号码",requestNo);
                                sendDetailSubService.updateBatchById(sendDetailSubs);
                            }
                    );
                }
            }
            response.getHeader().setResCode(code);
            response.getHeader().setResMessage(message);
            response.setBody(responseBody);

        } catch (Exception e) {
            log.error("慧声语音请求业务号requestNo:{},后置处理异常:{}", requestNo, e.getMessage(), e);
            throw new AfterOutboundException(e.getMessage(), e);
        }
    }



    

    /**
     * 数据保存
     * @param requestNo
     * @param sendDetailSubList
     */
    private void saveBatch(String requestNo,List<SendDetailSub> sendDetailSubList) {
        try {
            sendDetailSubService.saveBatch(sendDetailSubList);
        } catch (DuplicateKeyException e) {
            log.error("语音请求业务号requestNo:{},保存94语音明细数据,存在重复主键Id,违反唯一约束条件", requestNo);
            throw new RepetitionException(e);
        }
    }




    /**
     * 参数组装
     *
     *业务参数加签方式:
      加签方式RSA2,密钥为privateKey
      加签内容为post请求内容体
     *  @param request
     * @param sendDetailSubList
     * @return: 左边返回:头部参数、右边返回:请求体里加密的数据
     */
    private String buildParam(BaseRequest<VoiceRequest<VoiceExtend>> request, List<SendDetailSub> sendDetailSubList) throws Exception {
        //业务号
        String requestNo = request.getHeader().getRequestNo();
        //批次号
        String serialSeq = request.getHeader().getSerialSeq();
        //任务号，对应慧声语音caseId（场景ID）
        String taskId = request.getBody().getTaskId();

        LocalDateTime now = LocalDateTime.now();
        String timestamp = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        if (CollectionUtils.isEmpty(request.getBody().getSendDtl())){
            log.error("慧声语音请求业务号requestNo:{},requestId请求ID(批次号):{},请求参数的发送集合是空", requestNo, serialSeq);
            throw new BeforeOutboundException("慧声语音发送集合sendDtl字段为空!");
        }
        Map<String, String> requestParam = new HashMap<>();
        requestParam.put(Constants.REQ_APP_ID, appId);
        requestParam.put(Constants.REQ_METHOD,quickCall);
        requestParam.put(Constants.REQ_FORMAT,Constants.FORMAT);
        requestParam.put(Constants.REQ_CHARSET,Constants.CHARSET);
        requestParam.put(Constants.REQ_SIGN_TYPE,Constants.SIGN_TYPE);
        requestParam.put(Constants.REQ_VERSION,Constants.VERSION);
        requestParam.put(Constants.REQ_TIMESTAMP,timestamp);

        Map<String, Object> bizContent = new HashMap<>();
        List<JSONObject> customers = new ArrayList<>();
        request.getBody().getSendDtl().forEach(voiceParamItem -> {
            JSONObject customersItem = Optional.ofNullable(voiceParamItem.getProperties()).orElse(new JSONObject());
            //场景ID
            customersItem.put(Constants.REQ_SCENE_ID, taskId);
            //场景ID
            customersItem.put(Constants.REQ_PHONE_NUMBER, voiceParamItem.getCellPhoneNo());
            //唯一标识字段
            customersItem.put(Constants.REQ_CASE_ID, voiceParamItem.getUuid());
            customers.add(customersItem);

            SendDetailSub sendDetailSub = new SendDetailSub();
            //流水号,可以标识一个批次下的具体一条语音
            sendDetailSub.setUuid(voiceParamItem.getUuid());
            //请求业务号(上游传值)
            sendDetailSub.setRequestNo(requestNo);
            //请求号(批次号),blfs-sms生成
            sendDetailSub.setRequestId(serialSeq);
            //手机号
            sendDetailSub.setPhoneNumber(voiceParamItem.getCellPhoneNo());
            //场景ID
            sendDetailSub.setSceneId(taskId);
            sendDetailSub.setProperties(JSON.toJSONString(voiceParamItem.getProperties()));
            sendDetailSubList.add(sendDetailSub);
        });
        //请求参数
        bizContent.put("phoneNumbers",customers);
        requestParam.put(Constants.REQ_BIZ_CONTENT, JSON.toJSONString(bizContent));
        log.info("慧声语音请求业务号requestNo:{},请求号requestId(批次号):{},加密前的业务参数:{}", requestNo, serialSeq, JSON.toJSONString(requestParam));
        /**
         * 加密方式RSA2,密钥为privateKey
         * 加密内容为post请求内容体
         */
        //内容加签:
        String content = signatureService.getSignContent(requestParam);
        //签名
        String sign = signatureService.rsa256Sign(content);
        requestParam.put("sign", sign);

        log.info("慧声语音请求业务号requestNo:{},参数组织完成,加签后的内容:{}", requestNo, JSON.toJSONString(requestParam));
        return JSON.toJSONString(requestParam);
    }

    /**
     * 解析慧声语音响应,更新慧声语音明细表
     * @param requestNo 业务号
     * @param serialSeq 请求流水号
     * @param code
     * @param msg
     * @param subCode
     * @param subMsg
     */
    private void updateSendDetailSub(String requestNo,String serialSeq, String code,String msg, String subCode, String subMsg){
        UpdateWrapper<SendDetailSub> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(SendDetailSub::getCode, code)
                .set(SendDetailSub::getMsg, msg)
                .set(SendDetailSub::getSubCode, subCode)
                .set(SendDetailSub::getSubMsg, subMsg)
                .eq(SendDetailSub::getRequestNo, requestNo)
                //在blfs-sms模块生成的:请求id，具有唯一性(批次ID)、标识每一个批次
                .eq(SendDetailSub::getRequestId, serialSeq);
        sendDetailSubService.update(new SendDetailSub(), updateWrapper);
        log.info("慧声语音请求业务号requestNo:{},响应code字段:{},响应subCode字段:{},根据requestNo、requestId,更新慧声语音明细表数据",requestNo, code, subCode);
    }

    /**
     * 创建请求的HttpEntity
     * @param params
     * @return
     */
    private HttpEntity<String> creatRequestEntity(String params) {
        HttpHeaders headers = new HttpHeaders();
        //MediaType是 application/json;charset=UTF-8
        headers.setContentType(MediaType.parseMediaType(Constants.MEDIA_TYPE_APPLICATION_JSON_UTF8));
        return new HttpEntity<>(params, headers);
    }







    
}
